package org.courierframework.core.search;

import javax.persistence.Entity;
import java.util.*;

/**
 * @author long.shiyu
 * @since 1.7
 */
public abstract class Searchable<T> {
    private static final String ORDER = "order";
    private Integer start = 0;//default start index
    private Integer length = 5;//default page length
    private Integer draw = 0;
    private Map<String, Object[]> conditionsAndParameters = new LinkedHashMap<>();
    private List<Object> parameters = new ArrayList<>();

    public String generateCountJpql(Class<T> entityClz) {
        if (entityClz == null) {
            throw new IllegalArgumentException("[Assertion failed] - entity class argument can not be null");
        }
        return "select count(o) from " + getEntityName(entityClz) + " o ";
    }

    public String generateFilteredCountJpql(Class<T> entityClz) {
        if (entityClz == null) {
            throw new IllegalArgumentException("[Assertion failed] - entity class argument can not be null");
        }
        StringBuilder jpql = new StringBuilder("select count(o) from " + getEntityName(entityClz) + " o where 1=1 ");
        appender(jpql);
        return jpql.toString();
    }

    public String generateFilteredDataJpql(Class<T> entityClz) {
        if (entityClz == null) {
            throw new IllegalArgumentException("[Assertion failed] - entity class argument can not be null");
        }
        StringBuilder jpql = new StringBuilder("select o from " + getEntityName(entityClz) + " o where 1=1 ");
        appender(jpql);
        return jpql.toString();
    }

    protected abstract void addConditionsAndParameters(Map<String, Object[]> conditionsAndParameters);

    private void appender(StringBuilder jpql) {
        conditionsAndParameters.clear();
        parameters.clear();
        this.addConditionsAndParameters(conditionsAndParameters);
        for (Map.Entry<String, Object[]> entry : conditionsAndParameters.entrySet()) {
            String condition = entry.getKey();
            if (condition.startsWith(ORDER) || condition.startsWith(ORDER.toUpperCase())) {
                jpql.append(" ").append(entry.getKey()).append(" ");
            } else {
                jpql.append("and ").append(entry.getKey()).append(" ");
            }
            Object[] value = entry.getValue();
            if (value == null) {
                return;
            }
            parameters.addAll(Arrays.asList(value));
        }
    }

    private String getEntityName(Class<T> entityClz) {
        Entity entity = entityClz.getAnnotation(Entity.class);
        return entity != null && !"".equals(entity.name()) ? entity.name() : entityClz.getSimpleName();
    }

    public Integer getStart() {
        return start;
    }

    public void setStart(Integer start) {
        this.start = start;
    }

    public Integer getLength() {
        return length;
    }

    public void setLength(Integer length) {
        this.length = length;
    }

    public Integer getDraw() {
        return draw;
    }

    public void setDraw(Integer draw) {
        this.draw = draw;
    }

    public List<Object> getParameters() {
        return parameters;
    }
}

